#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.hpp"
using namespace std;
enum{
    UseError = 1,SubProNumError,PipeError
};

//管道
class Channel
{
    public:
    Channel(int wrt_fd,pid_t SubProcess_pid,const string& name)
    :_wrt_fd(wrt_fd),_SubProcess_pid(SubProcess_pid),_name(name)
    {}
    ~Channel()
    {}
    const string& name()
    {
        return _name;
    }
    const pid_t& pid()
    {
        return _SubProcess_pid;
    }
    const int& wrt_fd()
    {
        return _wrt_fd;
    }
    void Close(){close(_wrt_fd);}
private:
    //对于一个管道，有写端fd，有读端
    int _wrt_fd;//由于写端确定是主进程，只是区分主进程的fd，故定义为Int类型
    pid_t _SubProcess_pid;//读端有多个子进程
    string _name;//管道名
};

//进程池
class ProcessPool
{
public:
    ProcessPool(int SubProcess_num)
    :_SubProcess_num(SubProcess_num)
    {}
    ~ProcessPool()
    {}
    //创建多个子进程和管道
    int CreatProcess(work_type work)
    {
        vector<int> fd;
        for(int i = 1;i <= _SubProcess_num;++i)
        {
            //创建进程前，先创建管道
            int PipeFd[2]{0};
            int rtp = pipe(PipeFd);
            if(rtp < 0)
                return PipeError;
            pid_t id = fork();
            if(id ==0)
            {
                if(!fd.empty())
                {
                    std::cout << "close w fd: ";
                    for (auto i : fd)
                    {
                        close(i);
                        std::cout << i << " ";
                    }
                    std::cout << std::endl;
                }
                //子进程
                close(PipeFd[1]);
                
                //子进程从0号读，默认是键盘，现在重定向到管道读端
                dup2(PipeFd[0],0);
                work();
                exit(0);
                
            }
            //父进程
            close(PipeFd[0]);
            string CnnName = "channel-" + to_string(i);
            channels.push_back(Channel(PipeFd[1],id,CnnName));
            //把父进程的写fd记录，拷贝给子进程后要让子进程把多余的fd关闭
            fd.push_back(PipeFd[1]);

        }
        return 0;
    }
    //选择一个管道，选择方法是RR
    int SelectChannel()
    {
        static int next = 0;
        int ret = next;
        ++next;
        next %= channels.size();
        return ret;
    }
    //发送特定任务码给特定管道
    void SendTaskToChnn(int task_code,int IngChnn)
    {
        cout << "Send Code->" << task_code<<"To->"<< channels[IngChnn].name() << "SbuProId->" << channels[IngChnn].pid() <<"WirteFd->" << channels[IngChnn].wrt_fd() << endl;
        //主进程把任务写进管道
        write(channels[IngChnn].wrt_fd(),&task_code,sizeof(task_code));
    }
    //父进程控制子进程
    void CtlProcessPool(int cnt)
    {
        while(cnt)
         {
            //1.选择一个管道和子进程
            int one_channel = this->SelectChannel();
            //2.选择一个任务
            uint32_t one_task = SelectTask();
            //3.发送这个任务给管道
            this->SendTaskToChnn(one_task,one_channel);
            sleep(1);
            --cnt;
        }
    }
    //父进程回收全部进程
    void Kill_allPro()
    {
        for(auto& chann:channels)
        {
            //关闭写端，则读端自动关闭
            chann.Close();
            pid_t pid = chann.pid();
            pid_t rid = waitpid(pid,nullptr,0);

            if(rid == pid)
            {
                cout << "wait sub process->"<<pid<<"success"<<endl;
            }
            cout << chann.name() << "close done,sub process quit" << chann.pid() << endl;

        }
    }
private:
    //对于一个进程池，有多个管道和子进程。
    int _SubProcess_num;//这个进程池有多少个子进程
    vector<Channel> channels;//主进程用数据结构管理多个管道

};
int UseTips(const string& tip)
{
    cout << "UseTips:" << tip << " SubProcess_num" << endl;
    return UseError;
}
int main(int argc,char* argv[])
{
    //使用错误
    if(argc != 2)
    {
        UseTips(argv[0]);
        return UseError;
    }
    //子进程数量错误
    int SubProNum = stoi(argv[1]);
    if(SubProNum <= 0)
        return SubProNumError;
    //设置随机数种子
    srand((uint64_t)time(nullptr));
    //1.创建一个进程池
    ProcessPool* pp = new ProcessPool(SubProNum);
    pp->CreatProcess(worker);
    //2.父进程控制进程池
    pp->CtlProcessPool(10);
    //3.清理工作
    pp->Kill_allPro();

    delete pp;

    return 0;
}